﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using MoroMoro.Samples.ToDoList.Domain.Categories;

namespace MoroMoro.Samples.ToDoList.Domain.ToDos
{
    /// <summary>
    /// ToDo を表します。
    /// </summary>
    [Serializable]
    public sealed partial class ToDo
    {
        /// <summary>
        /// <see cref="ToDo"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        private ToDo()
        {
            Memos = new List<Memo>();
            RegisteredDateTime = DateTime.Now;
        }


        /// <summary>
        /// タイトルを取得または設定 (private) します。
        /// </summary>
        public string Title
        {
            get;
            private set;
        }

        /// <summary>
        /// <see cref="Category">カテゴリ</see>を取得または設定 (private) します。
        /// </summary>
        public Category Category
        {
            get;
            private set;
        }

        /// <summary>
        /// <see cref="Memo">メモ</see>の一覧を取得または設定 (private) します。
        /// </summary>
        internal IList<Memo> Memos
        {
            get;
            private set;
        }

        /// <summary>
        /// 登録日時を取得または設定 (private) します。
        /// </summary>
        public DateTime RegisteredDateTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 着手日時を取得または設定 (private) します。
        /// </summary>
        public DateTime? TouchedDateTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 完了日時を取得または設定 (private) します。
        /// </summary>
        public DateTime? CompletedDateTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 状態を取得します。
        /// </summary>
        public ToDoState State
        {
            get
            {
                return new ToDoStateViewpoint().Analyze(this);
            }
        }


        /// <summary>
        /// <see cref="Memo">メモ</see>の一覧を取得します。
        /// </summary>
        /// <returns><see cref="Memo">メモ</see>の一覧</returns>
        public ReadOnlyCollection<Memo> GetMemos()
        {
            return new ReadOnlyCollection<Memo>(Memos);
        }

        /// <summary>
        /// 現在の状態が指定した状態であるかどうかを示す値を取得します。
        /// </summary>
        /// <param name="state">状態。</param>
        /// <returns>現在の状態が指定した状態である場合は true、それ以外は false。</returns>
        public bool IsStateOf(ToDoState state)
        {
            return new ToDoStateSpecification(state).IsSatisfiedBy(this);
        }

        /// <summary>
        /// 現在の状態を指定した状態に進めることができるかどうかを示す値を取得します。
        /// </summary>
        /// <param name="state">状態。</param>
        /// <returns>現在の状態を指定した状態に進めることができる場合は true、それ以外は false。</returns>
        public bool CanForwardTo(ToDoState state)
        {
            return new ToDoCanForwardSpecification(state).IsSatisfiedBy(this);
        }

        /// <summary>
        /// 現在の状態を指定した状態に戻すことができるかどうかを示す値を取得します。
        /// </summary>
        /// <param name="state">状態。</param>
        /// <returns>現在の状態を指定した状態に戻すことができる場合は true、それ以外は false。</returns>
        public bool CanRevertTo(ToDoState state)
        {
            return new ToDoCanRevertSpecification(state).IsSatisfiedBy(this);
        }


        /// <summary>
        /// 現在の状態を指定した状態に進めます。
        /// </summary>
        /// <param name="state">状態。</param>
        /// <exception cref="System.InvalidOperationException">現在の状態からは指定した状態へ進めることができません。</exception>
        public void ForwardTo(ToDoState state)
        {
            if (!CanForwardTo(state))
            {
                throw new InvalidOperationException();
            }

            switch (state)
            {
                case ToDoState.Touched:
                    TouchedDateTime = DateTime.Now;
                    break;

                case ToDoState.Completed:
                    CompletedDateTime = DateTime.Now;
                    break;

                default:
                    throw new ArgumentException();
            }
        }

        /// <summary>
        /// 現在の状態を指定した状態に戻します。
        /// </summary>
        /// <param name="state">状態。</param>
        /// <exception cref="System.InvalidOperationException">現在の状態からは指定した状態へ戻すことができません。</exception>
        public void RevertTo(ToDoState state)
        {
            if (!CanRevertTo(state))
            {
                throw new InvalidOperationException();
            }

            switch (state)
            {
                case ToDoState.Untouched:
                    TouchedDateTime = null;
                    break;

                case ToDoState.Touched:
                    CompletedDateTime = null;
                    break;

                default:
                    throw new ArgumentException();
            }
        }
    }
}
